// 在ES6以前，js通过构造函数的方式来实现类的概念；在ES6中，可以直接使用class来声明一个类。ts在ES6的基础上，新增了类的一些特性
// 定义一个类，一般来说有属性、方法、构造函数，在通过new实例化类时，会自动调用构造函数。
// 静态属性和静态方法属于类，只能通过类访问，而不能通过类的实例访问
class Car {
  name: string; // 属性
  static age: number;
  constructor(name: string) {
    this.name = name;
  }; // 构造函数
  run(): void {}; // 方法
  static say(): void {}; // 静态方法
}
let car = new Car('car'); // 实例化

// 类的继承
// 继承采用关键词extends，子类继承父类的非私有属性和方法，子类中可通过super来访问父类的构造函数和方法
class ACar extends Car {
  constructor(name: string) {
    super(name)
  }
  run(): void {
    super.run();
  }
}


// 修饰符：public、private、protected
// 修饰符可用于修饰属性和方法，具体来说：
// public表示公有的，其修饰的属性和方法在任务地方都可以被访问，受限是最小的，默认为public
// private表示私有的，其修饰的属性和方法只能在当前类中使用，不能在其子类或者其他类中访问，受限是最大的
// protected表示受保护的，其修饰的属性和方法只能在当前类和子类中访问，受限适中
// 对于构造函数，如果用private修饰，表示不可被实例化，也不能被继承
class Car2 {
  private constructor(name: string) {}
}
// new Car2('hello'); // 编译错误
// 如果是用protected修饰构造函数，则表示不可被实例化，只能被继承
class Car3 {
  protected constructor(name: string) {}
}
class Car4 extends Car3 {}
// new Car3('hello'); // 编译错误


// abstract 抽象类和抽象方法
// 使用abstract修饰的类为抽象类，抽象类不可被实例化，只能用于继承。
// 抽象类可以有成员属性和成员方法，不一定有抽象方法；有抽象方法的类一定是抽象类。
// 抽象方法只能定义，不能有具体的实现；子类继承抽象类时，必需实现抽象类的抽象方法。
abstract class Car5 {
  name: string;
  constructor(name: string) {
    this.name = name
  };
  say():void {
    console.log('hello world');
  };
  abstract run(): void;
}
class Car6 extends Car5{
  run(): void {
    console.log('world hello'); // 子类必需实现抽象方法
  }
}

// 给类指定类型
// 我们可以给类的实例指定类型，常见于多态
abstract class Animal {
  abstract eat(): void;
}
class Cat extends Animal {
  eat(): void {
    console.log('cat eat');
  }
}
class Dog extends Animal {
  eat(): void {
    console.log('dog eat');
  }
}
let a:Animal = new Dog();
a = new Cat();
a.eat();